package com.zenika.zentracker.hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.util.Map;
import java.util.Set;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.transaction.TransactionManager;

import org.hibernate.ConnectionReleaseMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.MappingException;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.cache.Cache;
import org.hibernate.cache.QueryCache;
import org.hibernate.cache.UpdateTimestampsCache;
import org.hibernate.cfg.Settings;
import org.hibernate.classic.Session;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.engine.NamedQueryDefinition;
import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.query.QueryPlanCache;
import org.hibernate.exception.SQLExceptionConverter;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.stat.Statistics;
import org.hibernate.stat.StatisticsImplementor;
import org.hibernate.type.Type;
import org.springframework.context.ApplicationContext;

import com.zenika.zentracker.hibernate.interceptor.SessionStatisticsInterceptor;

public class ZenTrackerSessionFactory implements SessionFactory, SessionFactoryImplementor {

    private static final long serialVersionUID = -6175245548094344193L;

    private SessionFactory sessionFactory;
    private SessionFactoryImplementor sessionFactoryImpl;
    private ApplicationContext applicationContext;

    public ZenTrackerSessionFactory(final SessionFactory sessionFactory, final ApplicationContext applicationContext) {
        this.sessionFactory = sessionFactory;
        this.sessionFactoryImpl = (SessionFactoryImplementor) sessionFactory;
        this.applicationContext = applicationContext;
    }

    // #####################################
    // ########### SESSION FACTORY #########
    // #####################################

    public void close() throws HibernateException {
        sessionFactory.close();
    }

    public void evict(Class persistentClass, Serializable id) throws HibernateException {
        sessionFactory.evict(persistentClass, id);
    }

    public void evict(Class persistentClass) throws HibernateException {
        sessionFactory.evict(persistentClass);
    }

    public void evictCollection(String roleName, Serializable id) throws HibernateException {
        sessionFactory.evictCollection(roleName, id);
    }

    public void evictCollection(String roleName) throws HibernateException {
        sessionFactory.evictCollection(roleName);
    }

    public void evictEntity(String entityName, Serializable id) throws HibernateException {
        sessionFactory.evictEntity(entityName, id);
    }

    public void evictEntity(String entityName) throws HibernateException {
        sessionFactory.evictEntity(entityName);
    }

    public void evictQueries() throws HibernateException {
        sessionFactory.evictQueries();
    }

    public void evictQueries(String cacheRegion) throws HibernateException {
        sessionFactory.evictQueries(cacheRegion);
    }

    public Map getAllClassMetadata() throws HibernateException {
        return sessionFactory.getAllClassMetadata();
    }

    public Map getAllCollectionMetadata() throws HibernateException {
        return sessionFactory.getAllCollectionMetadata();
    }

    public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException {
        return sessionFactory.getClassMetadata(persistentClass);
    }

    public ClassMetadata getClassMetadata(String entityName) throws HibernateException {
        return sessionFactory.getClassMetadata(entityName);
    }

    public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException {
        return sessionFactory.getCollectionMetadata(roleName);
    }

    public Session getCurrentSession() throws HibernateException {
        return sessionFactory.getCurrentSession();
    }

    public Set getDefinedFilterNames() {
        return sessionFactory.getDefinedFilterNames();
    }

    public FilterDefinition getFilterDefinition(String filterName) throws HibernateException {
        return sessionFactory.getFilterDefinition(filterName);
    }

    public Reference getReference() throws NamingException {
        return sessionFactory.getReference();
    }

    public Statistics getStatistics() {
        return sessionFactory.getStatistics();
    }

    public boolean isClosed() {
        return sessionFactory.isClosed();
    }

    public Session openSession() throws HibernateException {
        return openSession(new SessionStatisticsInterceptor());
    }

    public Session openSession(Connection connection, Interceptor interceptor) {
        return new ZenTrackerSession(sessionFactory.openSession(connection, interceptor), applicationContext);
    }

    public Session openSession(Connection connection) {
        return openSession(connection, new SessionStatisticsInterceptor());
    }

    public Session openSession(Interceptor interceptor) throws HibernateException {
        return new ZenTrackerSession(sessionFactory.openSession(interceptor), applicationContext);
    }

    public StatelessSession openStatelessSession() {
        return sessionFactory.openStatelessSession();
    }

    public StatelessSession openStatelessSession(Connection connection) {
        return sessionFactory.openStatelessSession(connection);
    }

    // #################################################
    // ########### SESSION FACTORY IMPLEMENTOR #########
    // #################################################

    public Map getAllSecondLevelCacheRegions() {
        return sessionFactoryImpl.getAllSecondLevelCacheRegions();
    }

    public CollectionPersister getCollectionPersister(String role) throws MappingException {
        return sessionFactoryImpl.getCollectionPersister(role);
    }

    public Set getCollectionRolesByEntityParticipant(String entityName) {
        return sessionFactoryImpl.getCollectionRolesByEntityParticipant(entityName);
    }

    public ConnectionProvider getConnectionProvider() {
        return sessionFactoryImpl.getConnectionProvider();
    }

    public Dialect getDialect() {
        return sessionFactoryImpl.getDialect();
    }

    public EntityNotFoundDelegate getEntityNotFoundDelegate() {
        return sessionFactoryImpl.getEntityNotFoundDelegate();
    }

    public EntityPersister getEntityPersister(String entityName) throws MappingException {
        return sessionFactoryImpl.getEntityPersister(entityName);
    }

    public IdentifierGenerator getIdentifierGenerator(String rootEntityName) {
        return sessionFactoryImpl.getIdentifierGenerator(rootEntityName);
    }

    public String getIdentifierPropertyName(String className) throws MappingException {
        return sessionFactoryImpl.getIdentifierPropertyName(className);
    }

    public Type getIdentifierType(String className) throws MappingException {
        return sessionFactoryImpl.getIdentifierType(className);
    }

    public String[] getImplementors(String className) throws MappingException {
        return sessionFactoryImpl.getImplementors(className);
    }

    public String getImportedClassName(String name) {
        return sessionFactoryImpl.getImportedClassName(name);
    }

    public Interceptor getInterceptor() {
        return sessionFactoryImpl.getInterceptor();
    }

    public NamedQueryDefinition getNamedQuery(String queryName) {
        return sessionFactoryImpl.getNamedQuery(queryName);
    }

    public NamedSQLQueryDefinition getNamedSQLQuery(String queryName) {
        return sessionFactoryImpl.getNamedSQLQuery(queryName);
    }

    public QueryCache getQueryCache() {
        return sessionFactoryImpl.getQueryCache();
    }

    public QueryCache getQueryCache(String regionName) throws HibernateException {
        return sessionFactoryImpl.getQueryCache(regionName);
    }

    public QueryPlanCache getQueryPlanCache() {
        return sessionFactoryImpl.getQueryPlanCache();
    }

    public Type getReferencedPropertyType(String className, String propertyName) throws MappingException {
        return sessionFactoryImpl.getReferencedPropertyType(className, propertyName);
    }

    public ResultSetMappingDefinition getResultSetMapping(String name) {
        return sessionFactoryImpl.getResultSetMapping(name);
    }

    public String[] getReturnAliases(String queryString) throws HibernateException {
        return sessionFactoryImpl.getReturnAliases(queryString);
    }

    public Type[] getReturnTypes(String queryString) throws HibernateException {
        return sessionFactoryImpl.getReturnTypes(queryString);
    }

    public Cache getSecondLevelCacheRegion(String regionName) {
        return sessionFactoryImpl.getSecondLevelCacheRegion(regionName);
    }

    public Settings getSettings() {
        return sessionFactoryImpl.getSettings();
    }

    public SQLExceptionConverter getSQLExceptionConverter() {
        return sessionFactoryImpl.getSQLExceptionConverter();
    }

    public SQLFunctionRegistry getSqlFunctionRegistry() {
        return sessionFactoryImpl.getSqlFunctionRegistry();
    }

    public StatisticsImplementor getStatisticsImplementor() {
        return sessionFactoryImpl.getStatisticsImplementor();
    }

    public UpdateTimestampsCache getUpdateTimestampsCache() {
        return sessionFactoryImpl.getUpdateTimestampsCache();
    }

    public Session openSession(
            Connection connection,
            boolean flushBeforeCompletionEnabled,
            boolean autoCloseSessionEnabled,
            ConnectionReleaseMode connectionReleaseMode) throws HibernateException {
        return new ZenTrackerSession(sessionFactoryImpl.openSession(
                connection,
                flushBeforeCompletionEnabled,
                autoCloseSessionEnabled,
                connectionReleaseMode), applicationContext);
    }

    public Session openTemporarySession() throws HibernateException {
        return sessionFactoryImpl.openTemporarySession();
    }
    
    public TransactionManager getTransactionManager() {
        return sessionFactoryImpl.getTransactionManager();
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    
}
